package service

import (
	"context"
	"net/url"
	"strconv"
	"time"

	v1 "open/api/open/service/v1"
	"open/internal/model"
	"open/internal/model/event"
	"open/internal/model/field"
	"open/internal/types"
	"open/pkg/alipay"
	"open/pkg/log"
	wechat "gitee.com/quantum-force/wxopen-api"
	wechatCrypto "gitee.com/quantum-force/wxopen-api/crypto"

	"open/pkg/bytedance"

	"github.com/go-kratos/kratos/v2/errors"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"gorm.io/gorm"
)

// AccessToken 获取access_token
func (s *Service) AccessToken(ctx context.Context, req *v1.AccessTokenRequest) (*v1.AccessTokenReply, error) {
	switch req.Platform {
	case v1.Platform_PLATFORM_WECHAT:
		token, err := s.dao.GetWechatAuthorizerAccessToken(ctx, req.AppId)
		if err != nil {
			return nil, err
		}
		return &v1.AccessTokenReply{AccessToken: token}, nil
	}
	return nil, status.Errorf(codes.Unimplemented, "method AccessToken not implemented")
}

// ComponentAccessToken 获取component_access_token
func (s *Service) ComponentAccessToken(ctx context.Context, req *v1.ComponentAccessTokenRequest) (*v1.ComponentAccessTokenReply, error) {
	switch req.Platform {
	case v1.Platform_PLATFORM_WECHAT:
		_, token, err := s.dao.GetWechatComponentAccessToken(ctx)
		if err != nil {
			return nil, err
		}
		return &v1.ComponentAccessTokenReply{ComponentAccessToken: token}, nil
	case v1.Platform_PLATFORM_BYTEDANCE:
		_, token, err := s.dao.GetBytedanceComponentAccessToken(ctx)
		if err != nil {
			return nil, err
		}
		return &v1.ComponentAccessTokenReply{ComponentAccessToken: token}, nil
	}
	return nil, status.Errorf(codes.Unimplemented, "method AccessToken not implemented")
}

// CreateQRCodeTicket 创建带参数的二维码
func (s *Service) CreateQRCodeTicket(ctx context.Context, req *v1.CreateQRCodeTicketRequest) (*v1.CreateQRCodeTicketReply, error) {
	token, err := s.dao.GetWechatMPAccessToken(ctx, req.AppId)
	if err != nil {
		return nil, err
	}
	resp, err := s.dao.CreateWechatQRCodeTicket(ctx, token, req)
	if err != nil {
		return nil, err
	}
	return &v1.CreateQRCodeTicketReply{
		Ticket:        resp.Ticket,
		ExpireSeconds: resp.ExpireSeconds,
		Url:           resp.URL,
	}, nil
}

// SendWechatCustomMessage 发送客服消息
func (s *Service) SendWechatCustomMessage(ctx context.Context, req *v1.SendWechatCustomMessageRequest) (*v1.SendWechatCustomMessageReply, error) {
	token, err := s.dao.GetWechatMPAccessToken(ctx, req.AppId)
	if err != nil {
		return nil, err
	}
	if err := s.dao.SendWechatCustomMessage(ctx, token, req); err != nil {
		return nil, err
	}
	return &v1.SendWechatCustomMessageReply{Message: "ok"}, nil
}

// LoginWechatMini 登录微信小程序
func (s *Service) LoginWechatMini(ctx context.Context, req *v1.LoginWechatMiniRequest) (*v1.LoginWechatMiniReply, error) {
	subApp, err := s.dao.GetWechatSubApp(ctx, req.AppId)
	if err != nil {
		return nil, err
	}
	componentAppID, componentToken, err := s.dao.GetWechatComponentAccessToken(ctx)
	if err != nil {
		return nil, err
	}
	userInfo, err := s.dao.GetWechatSession(ctx, componentAppID, componentToken, subApp.AppID, req.Code)
	if err != nil {
		return nil, err
	}
	return &v1.LoginWechatMiniReply{
		OpenId:  userInfo.OpenID,
		UnionId: userInfo.UnionID,
	}, nil
}

// LoginBytedanceMini 登录字节跳动小程序
func (s *Service) LoginBytedanceMini(ctx context.Context, req *v1.LoginBytedanceMiniRequest) (*v1.LoginBytedanceMiniReply, error) {
	subApp, err := s.dao.GetBytedanceSubApp(ctx, req.AppId)
	if err != nil {
		return nil, err
	}
	session, err := s.dao.GetBytedanceSession(ctx, subApp.AppID, req.Code, req.AnonymousCode)
	if err != nil {
		if bdError, ok := err.(*bytedance.ErrorResponse); ok {
			switch bdError.ErrNo {
			case 40001:
				err = errors.BadRequest("INVALID_CODE", "参数错误")
			default:
				return nil, err
			}
		}
		return nil, err
	}
	return &v1.LoginBytedanceMiniReply{
		OpenId:          session.OpenID,
		AnonymousOpenId: session.AnonymousOpenID,
	}, nil
}

// LoginAlipayMini 登录支付宝小程序
func (s *Service) LoginAlipayMini(ctx context.Context, req *v1.LoginAlipayMiniRequest) (*v1.LoginAlipayMiniReply, error) {
	authInfo, err := s.dao.OauthAlipayUser(ctx, req.AppId, req.Code)
	if err != nil {
		if alipayError, ok := err.(*alipay.ErrorResponse); ok {
			return nil, errors.BadRequest("ALIPAY_ERROR", alipayError.SubMsg)
		}
		return nil, err
	}
	return &v1.LoginAlipayMiniReply{UserId: authInfo.UserID}, nil
}

// OauthMini 小程序登录
func (s *Service) OauthMini(ctx context.Context, req *v1.MiniOauthRequest) (*v1.MiniOauthReply, error) {
	switch req.Platform {
	case v1.Platform_PLATFORM_WECHAT:
		resp, err := s.LoginWechatMini(ctx, &v1.LoginWechatMiniRequest{
			AppId: req.AppId,
			Code:  req.Code,
		})
		if err != nil {
			return nil, err
		}
		return &v1.MiniOauthReply{
			OpenId:  resp.OpenId,
			UnionId: resp.UnionId,
		}, nil
	case v1.Platform_PLATFORM_ALIPAY:
		resp, err := s.LoginAlipayMini(ctx, &v1.LoginAlipayMiniRequest{
			AppId: req.AppId,
			Code:  req.Code,
		})
		if err != nil {
			return nil, err
		}
		return &v1.MiniOauthReply{
			OpenId: resp.UserId,
		}, nil
	case v1.Platform_PLATFORM_BYTEDANCE:
		resp, err := s.LoginBytedanceMini(ctx, &v1.LoginBytedanceMiniRequest{
			AppId: req.AppId,
			Code:  req.Code,
		})
		if err != nil {
			return nil, err
		}
		return &v1.MiniOauthReply{
			OpenId: resp.OpenId,
		}, nil
	default:
		return nil, status.Errorf(codes.Unimplemented, "platform not implemented")
	}

}

// OauthWechatMP Oauth 登录公众号
func (s *Service) OauthWechatMP(ctx context.Context, req *v1.OauthWechatMPRequest) (*v1.OauthWechatMPReply, error) {
	app, err := s.dao.GetWechatMPApp(ctx, req.AppId)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.NotFound("APP_NOT_FOUND", "微信公众号不存在")
		}
		return nil, err
	}
	resp, err := s.dao.OauthWechatMP(ctx, app.AppID, app.AppSecret, req.Code)
	if err != nil {
		if wechatError, ok := err.(*wechat.ErrorResponse); ok {
			return nil, errors.BadRequest("WECHAT_ERROR", wechatError.Message)
		}
		return nil, err
	}
	return &v1.OauthWechatMPReply{OpenId: resp.OpenID}, nil
}

// SendWechatMiniPubMessage 发送微信小程序订阅消息
func (s *Service) SendWechatMiniPubMessage(ctx context.Context, req *v1.SendWechatMiniPubMessageRequest) (*v1.SendWechatMiniPubMessageReply, error) {
	if err := s.dao.Transaction(ctx, func(ctx context.Context) error {
		subApp, err := s.dao.GetWechatSubApp(ctx, req.AppId)
		if err != nil {
			return err
		}
		schedule := model.Schedule{
			RefID:     subApp.ID,
			JobType:   types.TaskSendWechatPubMessage,
			JobData:   field.JSON("{}"),
			JobStatus: types.ScheduleTaskInit,
		}
		if err := s.dao.CreateSchedule(ctx, &schedule); err != nil {
			return err
		}
		if err := s.dao.TxSend(ctx, &event.TaskSendWechatPubMessage{
			JobID:   schedule.ID,
			AppID:   req.AppId,
			OpenID:  req.OpenId,
			MsgType: req.MsgType,
			Page:    req.Page,
			Data:    req.Data,
		}); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return nil, err
	}
	return &v1.SendWechatMiniPubMessageReply{}, nil
}

// GetWechatMPJsParams 获取公众号js params
func (s *Service) GetWechatMPJsParams(ctx context.Context, req *v1.GetWechatMPJsParamsRequest) (*v1.GetWechatMPJsParamsReply, error) {
	jsTicket, err := s.dao.GetWechatMPJsTicket(ctx, req.AppId)
	if err != nil {
		if wechatError, ok := err.(*wechat.ErrorResponse); ok {
			return nil, errors.BadRequest("WECHAT_ERROR", wechatError.Message)
		}
		return nil, err
	}
	nonceStr := wechatCrypto.RandString(32)
	timestamp := time.Now().Unix()
	values := url.Values{}
	values.Set("noncestr", nonceStr)
	values.Set("timestamp", strconv.Itoa(int(timestamp)))
	values.Set("jsapi_ticket", jsTicket)
	values.Set("url", req.Url)
	return &v1.GetWechatMPJsParamsReply{
		Noncestr:    nonceStr,
		JsapiTicket: jsTicket,
		Timestamp:   timestamp,
		Url:         req.Url,
		Sign:        wechatCrypto.JsSDKSign(values),
		AppId:       req.AppId,
	}, nil
}

// GetWechatPubTemplates 获取微信订阅消息列表
func (s *Service) GetWechatPubTemplates(ctx context.Context, req *v1.GetWechatPubTemplatesRequest) (*v1.GetWechatPubTemplatesReply, error) {
	tt, err := s.dao.GetWechatPubTemplates(ctx, req.Domain, req.AppId)
	if err != nil {
		return nil, err
	}
	templateIDs := make([]string, len(tt))
	for i, t := range tt {
		templateIDs[i] = t.TemplateID
	}
	return &v1.GetWechatPubTemplatesReply{TemplateIds: templateIDs}, nil
}

// DecryptWechatMiniData 解密微信小程序数据
func (s *Service) DecryptWechatMiniData(ctx context.Context, req *v1.DecryptWechatMiniDataRequest) (*v1.DecryptWechatMiniDataReply, error) {
	sessionKey, err := s.dao.GetWechatUserSessionKey(ctx, req.AppId, req.OpenId)
	if err != nil {
		return nil, err
	}
	if sessionKey == "" {
		return nil, errors.BadRequest("SESSION_KEY_NOT_FOUND", "session_key不存在")
	}
	decryptData, err := wechatCrypto.Decrypt(sessionKey, req.EncryptedData, req.Iv)
	if err != nil {
		return nil, errors.BadRequest("DECRYPT_ERROR", "解密数据出错")
	}
	return &v1.DecryptWechatMiniDataReply{DecryptedData: decryptData}, nil
}

// DecryptMiniData 解密小程序数据
func (s *Service) DecryptMiniData(ctx context.Context, req *v1.DecryptMiniDataRequest) (*v1.DecryptMiniDataReply, error) {
	var decryptData []byte
	switch req.Platform {
	case v1.Platform_PLATFORM_WECHAT:
		sessionKey, err := s.dao.GetWechatUserSessionKey(ctx, req.AppId, req.OpenId)
		if err != nil {
			return nil, err
		}
		if sessionKey == "" {
			return nil, errors.BadRequest("SESSION_KEY_NOT_FOUND", "session_key不存在")
		}
		decryptData, err = wechatCrypto.Decrypt(sessionKey, req.EncryptedData, req.Iv)
		if err != nil {
			return nil, errors.BadRequest("DECRYPT_ERROR", "解密数据出错")
		}
	case v1.Platform_PLATFORM_ALIPAY:
		encryptKey, err := s.dao.GetAlipayAseKey(ctx, req.AppId)
		if err != nil {
			return nil, err
		}
		decryptData, err = alipay.DecryptContent(req.EncryptedData, encryptKey)
		if err != nil {
			return nil, errors.BadRequest("DECRYPT_ERROR", "解密数据出错")
		}
	case v1.Platform_PLATFORM_BYTEDANCE:
		sessionKey, err := s.dao.GetBytedanceUserSessionKey(ctx, req.AppId, req.OpenId)
		if err != nil {
			return nil, err
		}
		if sessionKey == "" {
			return nil, errors.BadRequest("SESSION_KEY_NOT_FOUND", "session_key不存在")
		}
		decryptData, err = bytedance.DecryptContent(sessionKey, req.EncryptedData, req.Iv)
		if err != nil {
			return nil, errors.BadRequest("DECRYPT_ERROR", "解密数据出错")
		}
	}

	return &v1.DecryptMiniDataReply{DecryptedData: decryptData}, nil
}

// GetWechatMiniLivingRoom 获取正在直播的直播间RoomID
func (s *Service) GetWechatMiniLivingRoom(ctx context.Context, req *v1.GetWechatMiniLivingRoomRequest) (*v1.GetWechatMiniLivingRoomReply, error) {
	roomID, err := s.dao.GetWechatLivingRoomID(ctx, req.AppId)
	if err != nil {
		if wechatErr, ok := err.(*wechat.ErrorResponse); ok {
			err = errors.BadRequest("WECHAT_ERROR", wechatErr.Message)
		}
		log.FromContext(ctx).Debugf("微信获取直播列表失败, %v", err)
	}
	return &v1.GetWechatMiniLivingRoomReply{RoomId: roomID}, nil
}

// QueryWechatAppIDs 查询微信app_ids
func (s *Service) QueryWechatAppIDs(ctx context.Context, req *v1.QueryWechatAppIDsRequest) (*v1.QueryWechatAppIDsReply, error) {
	brandMiniApps, err := s.dao.FindWechatMiniAppByBrandIDs(ctx, req.BrandIds)
	if err != nil {
		return nil, err
	}
	m := make(map[int64]string, len(brandMiniApps))
	for _, app := range brandMiniApps {
		m[app.BrandID] = app.AppID
	}
	appIDs := make([]string, len(req.BrandIds))
	for i, brandID := range req.BrandIds {
		appIDs[i] = m[brandID]
	}
	return &v1.QueryWechatAppIDsReply{AppIds: appIDs}, nil
}
